#
# Sorto, the sorting game
# Copyright (C) 2012 Ralph Preston
# ralph.preston@gmail.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to:
#
# Free Software Foundation, Inc.
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301
# USA.
#
import xmlrpclib

from sorto_board  import SortoBoard
from sorto_record import SortoRecord

class SortoBasePlayer(object):
    
    def __init__(self, name='Player X', seed=1, view=None, color=None):
        """
        Creator should set their player name.
        """
        self._name   = name
        if color is None:
            self._color = '#ff0000'
        else:
            self._color  = color
            pass
        self._seed   = seed
        self._view   = view
        self._board  = SortoBoard()
        self._abort  = False
        self._available_counters = []
        self._game_number = 0
        self._round_count = 0
        self._game_record = SortoRecord()
        self._result_board = None
        self._result_counters = None
        return

    def abort(self):
        self._abort = True
        return

    def new_game(self, game_number):
        """
        The controller calls this at the start of every game. Use as
        you will.
        """
        self._game_number = game_number
        self._game_record.new_game()
        self._round_count = 0
        return

    def examine_table(self, players_boards, players_counters):
        """
        The player may view the table and examine other players boards
        and counters, in case this effects their decisions.

        players_boards is a dictionary using the players name as a key
        and a boards as the value. Ex: 
        { 
          'Player 1' : [None, None, 3, ... , None, None], 
          'Player 2' : [None, None, 3, ... , None, None], 
        }

        players_counters is a dictionary using the players name as a
        key and the value is a list of available counters.
        { 
          'Player 1' : [ 3, 5, 9 ], 
          'Player 2' : [ 3, 5, 9 ],
        }
        """
        raise Exception("You must overload this method")

    def play_roll(self, roll):
        """
        After receiving a game board and starting counters the
        controller begins generating a roll from 1 to 20.  The player
        must place/move one of their counters onto the board in the
        square rolled. If there are available counters the player must
        place one of them on the location rolled.

        The roll is an integer value from 1 to 20.
        """
        raise Exception("You must overload this method")

    def play_game_turn(self, game_number, round, roll, counters, 
                       other_players_boards, other_players_counters,
                       players_colors,
                       last_games_seed):
        """
        This is called from the controller.

        Breaks a game turn up into a number of steps which the player
        can customize.

        The steps are:
        1) new_game:  Pre-game initialization
        2) set_starting_board:  Setup an empty board
        3) set_starting_counters:  Record 2 starting counters
        4) next_counter:  Take a counter if any remain
        5) examine_table:  Look at other players boards
        6) play_roll:  Play the die roll
        7) record_game_seed:  For posterity
        8) record_score:  
        9) Continue to step 4 until a the game is over
        
        """
        if round == 0:
            if last_games_seed != 0:
                self._post_game_update(last_games_seed)
                pass
            self.new_game(game_number)
            self.set_starting_board(SortoBoard())
            self.set_starting_counters(counters)
        elif len(counters) == 1:
            self.next_counter(counters[0])
            pass

        self.examine_table(other_players_boards, other_players_counters)

        self.play_roll(roll)
        if self._abort: 
            return

        self._game_record.record_roll(self._game_number, roll)
        self._round_count += 1
        (self._result_board, self._result_counters) = self.get_board_state()
        return 

    def get_turn_results(self):
        return (self._result_board, self._result_counters)

    def game_over(self):
        """
        Yes, this is called when the game is over. When any player in
        the game satisfies the winning condition, or the round count
        reaches a threshold, this method is called on all players.
        """
        self._game_record.update_statistics()
        return

    def simulation_over(self, seed):
        """
        Called after simulation has completed all games.
        """
        self._post_game_update(seed)
        return

    def game_seed_was(self, seed):
        self._game_record.record_seed_for_game(self._game_number, seed)
        return
        
    def get_name(self):
        """
        Name your player. It's part of the fun.
        """
        return self._name

    def set_name(self, name):
        """
        The name of your player may be changed by the controller. It
        will only do this to make them unique if there are duplicate
        names.
        """
        self._name = name
        return
    
    def get_color(self):
        """
        The color for your counters
        """
        return self._color

    def get_seed(self):
        """
        The controller asks each player for a seed form the random
        number generator. The seeds are converted into strings and
        concatenated together. The string is then fed into SHA-256 to
        determine the value to use in random.seed().
        """
        return self._seed

    def set_starting_board(self, board):
        """
        The controller sets the player's starting board. A blank
        starting board is represented by a list with 36 elements of
        None. Ex: [ None ] * 36

        In the future it may be interesting to create 'challenge'
        starting boards where the controller generates a board with
        peices already placed for the players to solve.
        """
        self._board = board
        return

    def set_starting_counters(self, counters):
        """
        The controller determines the three counters to be used at the
        start of game. The counters are a list containing three
        integers ranging from 1 to 12. Ex: [ 1, 9, 12 ]
        """
        self._available_counters = counters
        self._game_record.record_counters_for_game(self._game_number, counters)
        return

    def next_counter(self, counter):
        """
        There are twelve counters for the player to place on the
        gameboard. The player begins a game with three (see
        set_starting_counters.) After a roll is played and a counter
        placed the player receives a new counter so they have three to
        choose from in the next round, until there are no counters
        remaining.

        The incoming counter is an integer from 1 to 12.
        """
        self._available_counters.append(counter)
        self._game_record.record_counters_for_game(self._game_number, counter)
        return
    
    def record_score(self, point_total):
        self._game_record.record_score(self._game_number, self._round_count, point_total)
        return

    def get_board_state(self):
        """
        After placing a counter the controller examins the state of
        all players boards and counters to determine if a player has
        won the round (and to catch cheaters!)
        
        There may be more than one winner in a round and players who
        did not win have their game board scored.
        """
        return (self._board, self._available_counters)
    
    def convert_roll_to_board_index(self, roll):
        """
        This converts a die roll into the indexes for the locations on
        the board. The indexes range from 0-35 so they can be used to
        lookup in a 36 element array used to represent the board. If
        the roll is 1,2,19,20 the second index is None.

        returns a two element tuple for the indexes. The second
        element is None if the roll was 1,2,19,20.
        """
        if roll < 3:
            res1 = roll - 1
            res2 = None
        elif roll < 20:
            _tmp = roll - 3
            _tmp = _tmp * 2
            res1 = _tmp + 2
            res2 = _tmp + 3
        else:
            res1 = 35
            pass
        if roll > 18:
            res2 = None
            pass
        return (res1, res2)

    def _post_game_update(self, last_games_seed):
        self.game_seed_was(last_games_seed)
        self.record_score(self._board.longest_chain())
        self.game_over()
        return

    pass # End of class SortoBasePlayer

